import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

/**
 * Rappresenta la risorsa dedicata alla gestione degli utenti registrati sul
 * sistema. È accessibile tramite il path relativo /users.
 * 
 */
@Path("/users")
public class UsersResource {

	/**
	 * Aggiunge un nuovo utente nel sistema, a meno che esso non sia già
	 * registrato. Tramite l’oggetto “user” è possibile specificare la lista di
	 * generi cinematografici per cui si desidera ricevere aggiornamenti
	 * all’uscita di un nuovo film.
	 * 
	 * @param user
	 *            Utente da registrare
	 * @return responso ( comprendente un codice di stato) sull'effettivo
	 *         compimento dell'operazione
	 */
	@PUT
	@Consumes({ MediaType.APPLICATION_XML })
	public Response registerUser(User user) {
		ResponseBuilder responseBuilder = Response.status(StatusCode.OK
				.getValue());
		MySQLConnection conn = null;
		try {

			conn = new MySQLConnection();

			// verifico presenza utente
			if (!QueryMaker.isUserRegistered(conn, user.getId())) {
				validateUserGenres(conn, user);
				QueryMaker.insertUser(conn, user);
			} else
				responseBuilder = Response
						.status(StatusCode.RESOURCE_ALREADY_EXISTING.getValue());

		} catch (SQLException e) {

			e.printStackTrace();
			responseBuilder = Response.serverError();// database error
		}
		if (conn != null)
			conn.closeConnection();
		return responseBuilder.build();
	}

	/**
	 * Elimina i generi non presenti nel database dall'oggetto user
	 * 
	 * @param conn
	 *            connessione da utilizzare per l'accesso al database
	 * @param user
	 *            utente la cui lista di generi a cui è registrato verrà
	 *            validata ed eventualmente modificata per corrispondere ai
	 *            generi presenti nel database. I generi non presenti nel
	 *            database verranno eliminati dalla lista dello user.
	 * @throws SQLException
	 */
	private void validateUserGenres(MySQLConnection conn, User user)
			throws SQLException {
		List<MovieType> serverGenres = QueryMaker.getGenres(conn);
		Iterator<MovieType> it = user.getMovieTypes().iterator();
		while (it.hasNext()) {
			String genre = it.next().getKey();

			int i;
			for (i = 0; i < serverGenres.size()
					&& !serverGenres.get(i).getKey().equals(genre); i++)
				;

			if (i >= serverGenres.size())
				it.remove();
		}
	}

	/**
	 * Dato l’username, identificativo di un utente, vengono restituiti tutti i
	 * generi cinematografici preferiti a cui è iscritto.
	 * 
	 * @param username
	 *            id dell'utente
	 * @return generi cinematografici preferiti
	 */
	@Path("/{username}/genres")
	@GET
	@Produces(MediaType.APPLICATION_XML)
	public Response getRegisteredGenres(@PathParam("username") String username) {
		ResponseBuilder responseBuilder = Response.status(StatusCode.OK
				.getValue());
		MySQLConnection conn = null;

		try {
			conn = new MySQLConnection();

			if (QueryMaker.isUserRegistered(conn, username)) {

				List<MovieType> genres = QueryMaker.getGenresByUser(conn,
						username);
				GenericEntity<List<MovieType>> genresEntity = new GenericEntity<List<MovieType>>(
						genres) {
				};
				responseBuilder.entity(genresEntity);

			} else
				responseBuilder = Response
						.status(StatusCode.RESOURCE_NOT_AVAILABLE.getValue());
		} catch (SQLException e) {

			e.printStackTrace();
			responseBuilder = Response.serverError();// database error
		}

		if (conn != null)
			conn.closeConnection();
		return responseBuilder.build();
	}

	/**
	 * Aggiorna con una nuova lista di generi cinematografici l’elenco di
	 * preferenze dell’utente.
	 * 
	 * @param username
	 *            id dell'utente
	 * @param types
	 *            nuova lista di generi cinematografici che sovrascriverà quella
	 *            preesistente nel database
	 * @return responso ( comprendente un codice di stato) sull'effettivo
	 *         compimento dell'operazione
	 */
	@Path("/{username}/genres")
	@POST
	@Consumes(MediaType.APPLICATION_XML)
	public Response updateGenres(@PathParam("username") String username,
			List<MovieType> types) {

		ResponseBuilder responseBuilder = Response.status(StatusCode.OK
				.getValue());
		MySQLConnection conn = null;

		try {
			conn = new MySQLConnection();

			if (QueryMaker.isUserRegistered(conn, username)) {

				User u = new User(username, types);
				validateUserGenres(conn, u);
				QueryMaker.updateUserGenres(conn, u);

			} else
				responseBuilder = Response
						.status(StatusCode.RESOURCE_NOT_AVAILABLE.getValue());
		} catch (SQLException e) {

			e.printStackTrace();
			responseBuilder = Response.serverError();// database error
		}

		if (conn != null)
			conn.closeConnection();
		return responseBuilder.build();
	}

	/**
	 * Ottiene la lista dei film inseriti nel database del server che
	 * appartengono allo stesso genere cinematografico a cui l’utente è
	 * interessato ma che non ha ancora visualizzato.
	 * 
	 * @param username
	 *            id dell'utente
	 * @return lista dei film dei generi preferiti non ancora visualizzati
	 */
	@Path("/{username}/news")
	@GET
	@Produces(MediaType.APPLICATION_XML)
	public Response getNews(@PathParam("username") String username) {
		ResponseBuilder responseBuilder = Response.status(StatusCode.OK
				.getValue());
		MySQLConnection conn = null;

		try {
			conn = new MySQLConnection();

			if (QueryMaker.isUserRegistered(conn, username)) {

				List<Movie> movies = QueryMaker.getNews(conn, username);

				GenericEntity<List<Movie>> moviesEntity = new GenericEntity<List<Movie>>(
						movies) {
				};
				responseBuilder.entity(moviesEntity);

			} else
				responseBuilder = Response
						.status(StatusCode.RESOURCE_NOT_AVAILABLE.getValue());
		} catch (SQLException e) {

			e.printStackTrace();
			responseBuilder = Response.serverError();// database error
		}

		if (conn != null)
			conn.closeConnection();
		return responseBuilder.build();
	}

	/**
	 * Ottiene la lista di tutti i film che appartengono allo stesso genere
	 * cinematografico a cui l’utente è interessato. Permette di ricevere solo
	 * un sottoinsieme del risultato, selezionando il numero di elementi
	 * (“length”) a partire da un indice di partenza rispetto all’elenco del
	 * risultato complessivo.
	 * 
	 * @param username
	 *            id dell'utente
	 * @param index
	 *            indice di partenza rispetto ai risultati complessivi
	 * @param length
	 *            numero di elementi da ricevere a partire dall'indice di
	 *            partenza "index"
	 * @return lista di film preferiti
	 */
	@Path("/{username}/movies")
	@GET
	@Produces(MediaType.APPLICATION_XML)
	public Response getMovies(@PathParam("username") String username,
			@DefaultValue("0") @QueryParam("startIndex") String index,
			@DefaultValue("0") @QueryParam("length") String length) {
		ResponseBuilder responseBuilder = Response.status(StatusCode.OK
				.getValue());
		MySQLConnection conn = null;

		try {
			int startIndex = Integer.parseInt(index);
			int reqLength = Integer.parseInt(length);
			if (startIndex < 0 || reqLength < 0) {
				responseBuilder = Response.status(StatusCode.PARAMETERS_ERROR
						.getValue());
			} else {// parametri validi

				conn = new MySQLConnection();

				if (QueryMaker.isUserRegistered(conn, username)) {

					List<Movie> movies = QueryMaker.getMoviesByUser(conn,
							username, startIndex, reqLength);
					GenericEntity<List<Movie>> moviesEntity = new GenericEntity<List<Movie>>(
							movies) {
					};
					responseBuilder.entity(moviesEntity);
					// se la lista è vuota, verifico se la causa è un errore
					// nell'uso degli indici/length
					if (movies.size() == 0) {
						// inizializzo con end of movies
						responseBuilder.status(StatusCode.OK_END_OF_MOVIES
								.getValue());
						// e faccio una seconda query
						int count = QueryMaker.getMoviesCountByUser(conn,
								username);
						if (count > 0)
							responseBuilder = Response
									.status(StatusCode.INDEX_OUT_OF_BOUNDS
											.getValue());
					}
					// caso End of movies
					else if (movies.size() < reqLength) {
						responseBuilder.status(StatusCode.OK_END_OF_MOVIES
								.getValue());
					}

				} else
					responseBuilder = Response
							.status(StatusCode.RESOURCE_NOT_AVAILABLE
									.getValue());
			}
		} catch (NumberFormatException e) {
			responseBuilder = Response.status(StatusCode.PARAMETERS_ERROR
					.getValue());
		} catch (Exception ex) {
			ex.printStackTrace();
			responseBuilder = Response.serverError();// database error
		}

		if (conn != null)
			conn.closeConnection();
		return responseBuilder.build();
	}
}
